package com.gorillalogic.scrummonkey.test {
	import com.gorillalogic.scrummonkey.dms.DmsUtils;
	import com.gorillalogic.scrummonkey.dms.Iteration;
	import com.gorillalogic.scrummonkey.dms.IterationService;
	import com.gorillalogic.scrummonkey.dms.Objective;
	import com.gorillalogic.scrummonkey.dms.ObjectiveService;
	import com.gorillalogic.scrummonkey.dms.Project;
	import com.gorillalogic.scrummonkey.dms.ProjectService;
	import com.gorillalogic.scrummonkey.dms.Release;
	import com.gorillalogic.scrummonkey.dms.ReleaseService;
	
	import mx.collections.ArrayCollection;
	
	import org.flexunit.Assert;

	/**
	 * Tests the following:
	 *   create P,R,I hierarchy
	 *   new O -> assigned only to P
	 *   reassign O on P to child I
	 */
	public class ObjectiveTest3 extends BaseTest {

		/**
		 * Create hierarchy: P with child R with child I
		 */
		[Test(async,order=1)]
		public function create():void {
			var p:Project = new Project('proj1','desc-proj1');
			var r:Release = new Release('rel1','desc-rel1');
			var i:Iteration = new Iteration('it1','desc-it1');

			//wireup hierarchy
			p.addRelease(r);
			r.addIteration(i);

			callAndThenOrFault(ProjectService.service.createProject(p), create2);
		}

		private function create2(data:Object, passThroughData:Object):void {
			callAndThenOrFault(ProjectService.service.getByName('proj1'), create3);
			callAndThenOrFault(ReleaseService.service.getByName('rel1'), create4);
		}

		private function create3(data:Object, passThroughData:Object):void {
			var project:Project = (data.result as ArrayCollection).getItemAt(0) as Project;
			Assert.assertEquals('Bad project name', 'proj1', project.name);
			Assert.assertEquals('Bad project description', 'desc-proj1', project.description);

			//verify release
			Assert.assertEquals('Bad number of releases', 1, project.releases.length);
			var release:Release = project.releases.getItemAt(0) as Release;
			Assert.assertEquals('Bad release name', 'rel1', release.name);
			Assert.assertEquals('Bad release description', 'desc-rel1', release.description);
		}

		private function create4(data:Object, passThroughData:Object):void {
			var release:Release = (data.result as ArrayCollection).getItemAt(0) as Release;
			Assert.assertEquals('Bad release name', 'rel1', release.name);
			Assert.assertEquals('Bad release description', 'desc-rel1', release.description);

			//verify iteration
			Assert.assertEquals('Bad number of iterations', 1, release.iterations.length);
			var iteration:Iteration = release.iterations.getItemAt(0) as Iteration;
			Assert.assertEquals('Bad iteration name', 'it1', iteration.name);
			Assert.assertEquals('Bad iteration description', 'desc-it1', iteration.description);
		}


		/**
		 * Add new objective to project only
		 */
		[Test(async,order=2)]
		public function createObjective():void {
			callAndThenOrFault(ProjectService.service.getByName('proj1'), createObjective2);
		}

		private function createObjective2(data:Object, passThroughData:Object):void {
			var project:Project = (data.result as ArrayCollection).getItemAt(0) as Project;
			Assert.assertEquals('Bad project name', 'proj1', project.name);

			//create new objective
			var objective:Objective = new Objective('objP','desc-objP');

			//Normally, we can just call project.addObjective() and forget about it,
			//but if we must guarantee completion...then use a transaction
			callAndThenOrFault(DmsUtils.doTxn(ProjectService.service,
				function ():void {
					project.addObjective(objective);
				}), createObjective3);
		}

		private function createObjective3(data:Object, passThroughData:Object):void {
			callAndThenOrFault(ProjectService.service.getByName('proj1'), createObjective4);
		}

		private function createObjective4(data:Object, passThroughData:Object):void {
			var project:Project = (data.result as ArrayCollection).getItemAt(0) as Project;
			Assert.assertEquals('Bad project name', 'proj1', project.name);

			Assert.assertEquals('Bad number of objectives', 1, project.objectives.length);
			var objective:Objective = project.objectives.getItemAt(0) as Objective;
			Assert.assertEquals('Bad objective name', 'objP', objective.name);
			Assert.assertEquals("Bad objective's project", 'proj1', objective.project.name);
			Assert.assertNull("Bad objective's release", objective.release);
			Assert.assertNull("Bad objective's iteration", objective.iteration);
		}


		/**
		 * Reassign O on P to child I
		 */
		[Test(async,order=3)]
		public function reassignObjective():void {
			callAndThenOrFault(ObjectiveService.service.getByName('objP'), reassignObjective2);
		}

		private function reassignObjective2(data:Object, passThroughData:Object):void {
			var objective:Objective = (data.result as ArrayCollection).getItemAt(0) as Objective;
			Assert.assertEquals('Bad objective name', 'objP', objective.name);
			Assert.assertEquals("Bad objective's project", 'proj1', objective.project.name);
			Assert.assertNull("Bad objective's release", objective.release);
			Assert.assertNull("Bad objective's iteration", objective.iteration);

			callAndThenOrFault(IterationService.service.getByName('it1'), reassignObjective3, null, 5000, objective);
		}

		private function reassignObjective3(data:Object, passThroughData:Object):void {
			var objective:Objective = passThroughData as Objective;
			Assert.assertEquals('Bad objective name', 'objP', objective.name);

			var iteration:Iteration = (data.result as ArrayCollection).getItemAt(0) as Iteration;
			Assert.assertEquals('Bad iteration name', 'it1', iteration.name);
			Assert.assertEquals("Bad iteration's release name", 'rel1', iteration.release.name);

			//Normally, we can just call objective.assign() and forget about it,
			//but if we must guarantee completion...then use a transaction
			callAndThenOrFault(DmsUtils.doTxn(ProjectService.service,
				function ():void {
					objective.assign(iteration);
				}), reassignObjective4);
		}

		private function reassignObjective4(data:Object, passThroughData:Object):void {
			callAndThenOrFault(ObjectiveService.service.getByName('objP'), reassignObjective5);
		}

		private function reassignObjective5(data:Object, passThroughData:Object):void {
			var objective:Objective = (data.result as ArrayCollection).getItemAt(0) as Objective;
			Assert.assertEquals('Bad objective name', 'objP', objective.name);
			Assert.assertEquals("Bad objective's project", 'proj1', objective.project.name);
			Assert.assertEquals("Bad objective's release", 'rel1', objective.release.name);
			Assert.assertEquals("Bad objective's iteration", 'it1', objective.iteration.name);
		}


		/**
		 * Cleanup everything
		 */
		[Test(async,order=4)]
		public function cleanup():void {
			callAndThenOrFault(ProjectService.service.getByName('proj1'), cleanup2);
		}

		private function cleanup2(data:Object, passThroughData:Object):void {
			var project:Project = (data.result as ArrayCollection).getItemAt(0) as Project;
			Assert.assertEquals('Bad project name', 'proj1', project.name);

			callAndThenOrFault(ProjectService.service.deleteProject(project), cleanup3);
		}

		private function cleanup3(data:Object, passThroughData:Object):void {
			callAndThenOrFault(ProjectService.service.getByName('proj1'), cleanup4);
			callAndThenOrFault(ReleaseService.service.getByName('rel1'), cleanup4);
			callAndThenOrFault(IterationService.service.getByName('it1'), cleanup4);
			callAndThenOrFault(ObjectiveService.service.getByName('objP'), cleanup4);
		}

		private function cleanup4(data:Object, passThroughData:Object):void {
			var entities:ArrayCollection = data.result as ArrayCollection;
			Assert.assertEquals('Cleanup failed', 0, entities.length);
		}
	}
}